package com.example.factory;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;
import org.dom4j.xpath.DefaultXPath;

import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

//这个是我们对应工厂的接口实现类
//相当于Spring的  ClassPathXmlApplicationContext 类
/*
    1、我们要读取我们的配置文件，也就是将我们的配置文件读取出来；
      我们通过这个类的构造函数的方法传过来
    2、然后我们要解析这个配置文件，得到每一个bean标签的id属性值和class属性值，并设置到对应的MyBean对象中，再存到集合中
    3、然后我们要遍历这个集合，得到每一个class属性对应的实例化对象，然后设置到Map中，id值多为key，这个实例化对象作为value
    4、我们会通过getBean方法，从Map对象中通过id获取指定的value，value也就是实例化对象

 */
public class MyClassPathXmlApplicationContext implements MyFactory{

    //我们首先定义一个 Map对象  用来存放id属性和对应实例化好的 Bean对象
    private Map<String,Object> beanMap=new HashMap<>();
    //定义List集合，用来存放配置文件中bean标签对应的id与class属性值得 MyBean对象
    private List<MyBean> beanList=null;


    //1、我们要读取我们的配置文件，也就是将我们的配置文件读取出来；
    //      我们通过这个类的构造函数的方法传过来
    public MyClassPathXmlApplicationContext(String fileName){
        parseXml(fileName);

        instanceBean();
    }

    //3、然后我们要遍历这个集合，得到每一个class属性对应的实例化对象，然后设置到Map中，id值多为key，这个实例化对象作为value
    private void instanceBean() {
        //我们首先判断我们的  beanList是否为空？
        if (beanList!=null&&beanList.size()>0){
            //这里我们遍历beanList集合，得到对应的  MyBean对象
            for (MyBean myBean : beanList) {
                //得到id值
                String id = myBean.getId();
                //得到class值
                String clazz = myBean.getClazz();
                //接下来我们要通过反射，实例化指定class属性值对应的Bean对象
                try {
                    //这里多说一下，下面的这条语句更好，是目前推荐的
                    //第二行代码从 JAVA 9开始已经不推荐了，使用的时候你会看到花了一条线
                    Object object = Class.forName(clazz).getDeclaredConstructor().newInstance();
                   //  Object object = Class.forName(clazz).newInstance();
                    //将我们的id值与实例化好的bean对象，设置到map中
                    beanMap.put(id,object);
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                }
            }

        }


    }

    //  2、然后我们要解析这个配置文件，得到每一个bean标签的id属性值和class属性值，并设置到对应的MyBean对象中，再存到集合中
    private void parseXml(String fileName) {
        try {
        //首先得到我们的解析器  是在我们的  dom4j  jar包里面的
        SAXReader reader=new SAXReader();
        //下面我们还要得到配置文件的  URL   这个代码我还不是很理解， 需要查一下
        URL url = this.getClass().getClassLoader().getResource(fileName);
        //下面我们要解析配置文件，得到一个 Document对象
            Document document = reader.read(url);
            //这个文档对象我们就需要去解析，  这里我们会使用到  XPath语法
            //这里我们可以到菜鸟教程上看一看关于  XPath的介绍，简单的学习一下
            //我们肯定要获取 beans标签下的所有的  bean标签
            //XPath使用路径表达式来选取 XML文档中的 节点或者是节点集   这个XPath是一个接口
            XPath xPath = document.createXPath("beans/bean");
            //上面我们是需要这个  文档创建一个 有特殊功能的XPath对象以后，由这个对象按照它的特殊功能操作这个文档

            //下面我们通过XPath语法得到对应的bean标签， 会返回一个  Element集合  Element也是dom4j里面的对象
            List<Element> elementList=xPath.selectNodes(document);
            //我们要先判断一下这个集合是否为  空
            if (elementList!=null&&elementList.size()>0){
                beanList=new ArrayList<>();//因为我们上面没有实例化
                //这里我们遍历Element集合，得到Element对象，得到标签对应的属性值
                for (Element element : elementList) {
                    //首先得到id属性的值  attributeValue：通过指定属性名得到对应的属性值
                    String id=element.attributeValue("id");
                    String clazz=element.attributeValue("class");
                    //下面将我们的 id属性值和class属性值设置到我们的一个 MyBean对象中
                    MyBean myBean=new MyBean(id,clazz);
                    beanList.add(myBean);//将这个MyBean对象添加到集合里面
                }
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }

    }

    // 4、我们会通过getBean方法，从Map对象中通过id获取指定的value，value也就是实例化对象

    @Override
    public Object getBean(String id) {
        //这个就是通过id，从我们的Map对象中获取对应的实例化对象
        Object object = beanMap.get(id);
        return object;
    }
}
